#include "BlockQueue.hpp"
#include "Task.hpp"
#include <mutex>
// endl和\n区别: endl会强制刷新缓冲区 \n不会

mutex cout_mutex;  // 全局互斥锁 保护cout

template<class C, class S>
class BlockQueues
{
public:
    BlockQueue<C> *c_bq;
    BlockQueue<S> *s_bq;
};

void *productor(void *bqs_)
{
    BlockQueue<CalTask> *bq = (static_cast<BlockQueues<CalTask, SaveTask>*>(bqs_))->c_bq;
    while (true)
    {
        int x = rand() % 10;  
        int y = rand() % 10;
        int operCode = rand() % oper.size(); //0-3
        CalTask t (x, y, oper[operCode], mymath);

        bq->push(t);
        {
            lock_guard<mutex> lock(cout_mutex);  // 加锁保护 cout
            cout << "productor thread 生产计算任务: " << t.TasktoString() << "\n";
        }
        //sleep(1);
    }
    return nullptr;
}

void *consumer(void *bqs_)
{
    BlockQueue<CalTask> *bq = (static_cast<BlockQueues<CalTask, SaveTask> *>(bqs_))->c_bq;
    BlockQueue<SaveTask> *save_bq = (static_cast<BlockQueues<CalTask, SaveTask> *>(bqs_))->s_bq;

    while (true)
    {
        CalTask t; // 创建一个CalTask对象
        bq->pop(&t); // 从阻塞队列中取出任务拷贝到t内
        string result = t(); // 执行计算任务并获取结果
        {
            lock_guard<mutex> lock(cout_mutex);  // 加锁保护 cout
            cout << "cal thread 完成计算任务: " << result << "\n";
        }

        SaveTask save (result, Save);
        save_bq->push(save);
        {
            lock_guard<mutex> lock(cout_mutex);  // 加锁保护 cout
            cout << "cal thread 推送存储任务完成...\n";
        }
        sleep(1);
    }
    return nullptr;
}

void *saver(void *bqs_)
{
    BlockQueue<SaveTask> *save_bq = (static_cast<BlockQueues<CalTask, SaveTask> *>(bqs_))->s_bq;

    while(true)
    {
        SaveTask t; // 创建一个SaveTask对象
        save_bq->pop(&t); // 从阻塞队列中取出任务拷贝到t内
        t();  // 执行计算任务并获取结果
        {
            lock_guard<mutex> lock(cout_mutex);  // 加锁保护 cout
            cout << "save thread 保存任务完成...\n";
        }
    }
    return nullptr;
}

int main()
{
    srand((unsigned long)time(nullptr) ^666);

    BlockQueues<CalTask, SaveTask> bqs;
    bqs.c_bq = new BlockQueue<CalTask>();
    bqs.s_bq = new BlockQueue<SaveTask>();

    pthread_t c[2], p[3], s;

    pthread_create(p, nullptr, productor, &bqs);
    pthread_create(p+1, nullptr, productor, &bqs);
    pthread_create(p+2, nullptr, productor, &bqs);
    pthread_create(c, nullptr, consumer, &bqs);
    pthread_create(c+1, nullptr, consumer, &bqs);
    pthread_create(&s, nullptr, saver, &bqs);

    
    pthread_join(p[0], nullptr);
    pthread_join(p[1], nullptr);
    pthread_join(p[2], nullptr);
    pthread_join(c[0], nullptr);
    pthread_join(c[1], nullptr);
    pthread_join(s, nullptr);

    delete bqs.c_bq;
    delete bqs.s_bq;
    return 0;
}